using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using Pampa.CRUD.Core;
using Pampa.CRUD.Configuration;
using Pampa.CRUD.UI.Exceptions;
using Pampa.CRUD.Security;
using Pampa.CRUD.Utils;
using Pampa.WebControls;

namespace Pampa.CRUD.UI.CustomControls
{
    public enum GroupOrientationType
    { 
        Horizontal, Vertical
    }
    
    [ToolboxData("<{0}:EntityView runat=server></{0}:EntityView>")]
    [DesignerAttribute(typeof(BaseDesigner), typeof(System.ComponentModel.Design.IDesigner))]
    public class EntityView : EntityControl
    {
        GroupOrientationType groupOrientation;

        public EntityView()
        {
            this.CssClass = "entityViewDefault";
            this.GroupOrientation = GroupOrientationType.Vertical;
        }

        #region Properties

        public GroupOrientationType GroupOrientation
        {
            get { return groupOrientation; }
            set { groupOrientation = value; }
        }

        public override EntityConfiguration Configuration
        {
            get
            {
                return base.Configuration;
            }
            set
            {
                Session.SaveConfig(this.EntityName, value);
                base.Configuration = value;
            }
        }

        #endregion
        
        #region Load

        protected override void OnLoad(EventArgs e)
        {
            Ajax.Utility.RegisterTypeForAjax(typeof(EntityView));

            base.OnLoad(e);

            if (this.CssClass == "entityViewDefault")
                RegisterStyleSheet("Pampa.CRUD/EntityView.css");

            RegisterLibrary("Pampa.CRUD/EntityView.js");
        }

        #endregion

        #region Render

        protected override void Render(HtmlTextWriter writer)
        {
            writer.AddAttribute("id", this.ID);
            writer.AddAttribute("class", this.CssClass);

            // No renderizo el higth (este depende del contenido que tome el componente)
            if (!string.IsNullOrEmpty(this.Width.ToString()))
                writer.AddStyleAttribute("width", this.Width.ToString());

            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            RenderContents(writer);
            writer.RenderEndTag();

            RenderDefinitions(writer);
        }

        protected void RenderDefinitions(HtmlTextWriter output)
        {
            string objectName = "ev" + this.ID;

            output.WriteLine("<script type=\"text/javascript\">");
            output.WriteLine("var " + objectName + " = new WebEntityView(\"" + this.ID + "\");");
            output.WriteLine(objectName + ".entityName = \"" + this.EntityName + "\";");
            output.WriteLine(objectName + ".orientation = \"" + this.GroupOrientation + "\";");
            output.WriteLine("</script>");
        }

        protected override void RenderContents(HtmlTextWriter output)
        {
            if (this.Entity == null)
                return;

            if (this.Configuration == null)
                LoadConfiguration();

            output.Write(GetHTML(this.Entity, this.Configuration, this.GroupOrientation, this.SecurityManager));
        }

        #endregion

        #region Ajax Functions

        [Ajax.AjaxMethod(Ajax.HttpSessionStateRequirement.ReadWrite)]
        public static string GetHTML(string entityName, string entityID, string strOrientation)
        {
            var entityType = Type.GetType(entityName);
            var entity = EntityManager.LoadFromStringId(entityType, entityID);
            var configuration = Session.GetConfig(entityName);
            var orientation = (GroupOrientationType)Enum.Parse(typeof(GroupOrientationType), strOrientation);

            // refrescando la vista desde el cliente no se tendran en cuenta los permisos asociados
            // TODO: eventualmente podria pasarse el nombre de la clase de seguridad asociada para instanciarla y tener en cuenta los permisos

            if ((entity != null) && (configuration != null))
                return GetHTML(entity, configuration, orientation, null);
            else
                return "&nbsp;";
        }

        #endregion

        #region Private Region

        private void LoadConfiguration()
        {
            Type entityType = this.Entity.GetType();
            this.Configuration = ConfigurationManager.GetViewConfiguration(entityType.AssemblyQualifiedName);
        }

        private static string GetHTML(object entity, EntityConfiguration configuration, GroupOrientationType orientation, ISecurityManager securityManager)
        {
            string html = "";

            if (orientation == GroupOrientationType.Horizontal)
            {
                html += "<table cellspacing=0 cellpadding=0 style=\"width:100%\">";
                html += "<tr>";
            }

            // Parseo el config y contruyo tantos editablegroup como hagan falta
            foreach (Group group in configuration.Groups)
            {
                if (IsPermitted(entity, group.PermissionRequired, securityManager))
                {
                    if (group is HeaderGroup)
                        html += GetHTMLHeaderGroup(entity, (HeaderGroup)group, orientation, securityManager);

                    if (orientation == GroupOrientationType.Vertical)
                        html += "<br />";
                }
            }

            if (orientation == GroupOrientationType.Horizontal)
            {
                html += "</tr>";
                html += "</table>";
            }

            return html;
        }

        private static string GetHTMLHeaderGroup(object entity, HeaderGroup group, GroupOrientationType orientation, ISecurityManager securityManager)
        {
            string html = "";

            if (orientation == GroupOrientationType.Horizontal)
            {
                html += "<td style=\"vertical-align:top\">";
            }

            // Titulo
            html += "<table cellspacing=0 cellpadding=0 style=\"width:100%\" class=\"groupTable\">";
            html += "<tr><td class=\"title\" colspan=\"2\">" + group.Title + "</td></tr>";

            // Items
            foreach (Item item in group.Items)
            {
                if(item is ViewItem)
                {
                    if (IsPermitted(entity, ((ViewItem)item).PermissionRequired, securityManager))
                        html += GetHTMLViewItem(entity, (ViewItem)item);
                }
            }

            html += "</table>";

            if (orientation == GroupOrientationType.Horizontal)
            {
                html += "</td><td>&nbsp</td>";
            }

            return html;
        }

        private static string GetHTMLViewItem(object entity, ViewItem viewItem)
        {
            string html = "";

            viewItem.Value = EntityManager.GetPropertyValue(entity, viewItem.Id);

            html += "<tr>";
            html += "<td class=\"caption\">" + viewItem.Label + "</td>";
            html += "<td class=\"value\">";
            html += viewItem.StringValue;
            html += "&nbsp;";
            html += "</td></tr>";

            return html;
        }

        /// <summary>
        /// Esta funcion determina a traves del SecurityManager si el usuario logeado tiene permisos para ver el item o el grupo en cuestion
        /// Si no hay un SecurityManager asociado devuelve siempre true
        /// Si no hay permisos asociados al item (PermissionType.NotApply) devuelve true
        /// (Los permisos de edicion sobre un item de tipo ViewItem son equivalentes a tener permisos de solo lectura)
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        private static bool IsPermitted(object entity, string permissionRequired, ISecurityManager securityManager)
        {
            if (securityManager == null)
            {
                return true;
            }
            else if (string.IsNullOrEmpty(permissionRequired))
            {
                return true;
            }
            else
            {
                PermissionType permissionType = securityManager.GetPermission(entity, permissionRequired);
                return (permissionType != PermissionType.Rejected);
            }
        }

        #endregion

        #region MVC Hook

        public static string Includes()
        {
            // avoid having registered the library more than once
            if (HttpContext.Current.Items.Contains("Pampa.CRUD.EntityView"))
            {
                return "";
            }
            else
            {
                var sb = new StringBuilder();
                sb.Append(IncludeLibrary("Pampa.CRUD/EntityView.js"));

                HttpContext.Current.Items.Add("Pampa.CRUD.EntityView", true);

                return sb.ToString();
            }
        }

        #endregion
    }
}
